#pragma once
#include <list>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <unordered_map>
#include <vector>

template <class CB>
class task_pool
{
public:
    //1. 插入任务
    void push(const CB &cb);
    //2. 获取任务
    CB & get(size_t n);
    //3. 返回任务池大小
    size_t size() {return _task_pool.size();}
protected:  
    std::vector<CB> _task_pool;
};

//1. 插入任务
template <class CB>
void task_pool<CB>::push(const CB &cb)
{
    _task_pool.push_back(cb);
}
//2. 获取任务
template <class CB>
CB &task_pool<CB>::get(size_t n)
{
    return _task_pool[n];
}


template<class CB>
class process_pool
{
public:
    process_pool(task_pool<CB> *tp,size_t pnum = 5);
    //1. 启动进程池，创建一批进程
    void start();
    //2. 发送信号，子进程执行任务
    void send_signal();
protected:
    task_pool<CB> *_tp;
    size_t _pnum;
    size_t _pid;
    std::unordered_map<size_t,int> _pid_pipefd;
};

template<class CB>
process_pool<CB>::process_pool(task_pool<CB> *tp,size_t pnum)
    :_tp(tp),_pnum(pnum),_pid(0)
{}

//1. 启动进程池，开始执行任务
template<class CB>
void process_pool<CB>::start()
{
    int cnt = _pnum;
    while(cnt--)
    {
        //1. 打开管道读写端口
        int pipefd[2];
        int n = pipe(pipefd);
        if(n == -1)
        {
            std::cout << "管道端口创建失败！" << std::endl;
            exit(-1);
        }
        //2. 创建n个进程
        pid_t pid = fork();
        if(pid == 0)
        {
            //  2.1 子进程关闭写端口
            close(pipefd[1]);
            //  2.2 子进程等待父进程的信号
           while(true)
           {
                size_t pos = -1;
                int n = read(pipefd[0],&pos,sizeof(pos));
                if(n > 0)
                {
                    //  2.3 接收到信号，执行任务
                    CB cb = _tp->get(pos);
                    std::cout << getpid() << "号进程执行任务:";
                    cb();
                    std::cout << std::endl;
                }
           }
        }
        //3. 父进程关闭读端
        close(pipefd[0]);   
        _pid_pipefd.insert(std::make_pair(_pid,pipefd[1]));
        ++_pid;
    }
    //4. 发送任务执行的信号
    send_signal();
}


//2. 发送信号，子进程执行任务
template<class CB>
void process_pool<CB>::send_signal()
{
    while(true)
    {
        //1. 随机选取一个进程
        size_t pid = rand() % _pid;
        //2. 随机选取一个任务
        size_t tid = rand() % _tp->size(); 
        //3. 获取进程对应的管道写端
        auto it = _pid_pipefd.find(pid);
        if(it == _pid_pipefd.end())
        {
            std::cout << "任务发送失败！" << std::endl;
            continue;
        }
        int write_fd = it->second;
        //4. 向管道写入任务号
        write(write_fd,&tid,sizeof(tid));
        //5. 没隔一秒发送一个任务
        sleep(1);
    }
}